package org.orbis.communicate.filter;

import java.util.Arrays;
import java.util.BitSet;
import org.orbis.communicate.FlagsResult;

public class SimpleFilter implements Filter<FlagsResult[], long[]>{
	@Override
	public FlagsResult[] intersection(long[] dummy, FlagsResult[]... part){
		// ArrayList<Long> results = new ArrayList<Long>(part.length * part[0].length);
		if(part == null) return null;
		int parts = part.length;
		int tlength = 0;
		for(int i = 0; i < parts; i++){
			if(part[i] == null) return null;
			if(tlength < part[i].length){
				tlength = part[i].length;
			}
		}
		FlagsResult[] result = new FlagsResult[tlength];

		int count;
		BitSet isEnd = new BitSet(parts);
		int[] currentPoint = new int[parts];
		for(int i = 0; i < parts; i++){
			if(part[i] == null) isEnd.set(i);
			currentPoint[i] = 0;
		}

		count = 0;
		while(isEnd.cardinality() == 0){
			boolean isSame = true;
			long maximum = Long.MIN_VALUE;
			int index = 0;
			for(int i = 0; i < parts; i++){
//				System.out.printf("Point: %d, ID: %d%n", currentPoint[i], part[i][currentPoint[i]].ID());
				if(part[i][currentPoint[i]] == null) continue;
				if(maximum < part[i][currentPoint[i]].ID()){
					index = i;
					maximum = part[i][currentPoint[i]].ID();
				}
				if(i == parts - 1) break;
				if(part[i][currentPoint[i]].ID() != part[i + 1][currentPoint[i + 1]].ID())
					isSame = false;
			}
//			System.out.printf("Same? %s %n", Boolean.toString(isSame));

			if(isSame){
				// Same pointer is found
				result[count++] = part[0][currentPoint[0]];
				for(int i = 0; i < parts; i++){
					currentPoint[i]++;
					if(currentPoint[i] >= part[i].length) isEnd.set(i);
				}
			}else{
				currentPoint[index]++;
				if(currentPoint[index] >= part[index].length) isEnd.set(index);
			}
		}

		FlagsResult[] newResult = Arrays.copyOf(result, count);

		return newResult;
	}

	@Override
	public FlagsResult[] join(long[] dummy, FlagsResult[]... part){
		if(part == null) return null;
		int parts = part.length;
		int tlength = 0;
		for(int i = 0; i < parts; i++){
			if(part[i] == null) return null;
			tlength += part[i].length;
		}

		FlagsResult[] results = new FlagsResult[tlength];

		int[] currentIndex = new int[parts];
		BitSet reachEnd = new BitSet(parts);
		for(int i = 0; i < parts; i++){
			if(part[i] == null) reachEnd.set(i);
			currentIndex[i] = 0;
		}

		// Stop if all parts get end point
		int count = 0;
		while(reachEnd.cardinality() < parts){
			// Find 'first' ID
			long minimum = Long.MAX_VALUE;
			int index = -1;
			for(int i = 0; i < parts; i++){
				if(reachEnd.get(i)) continue;
				if(minimum > part[i][currentIndex[i]].ID()){
					minimum = part[i][currentIndex[i]].ID();
					index = i;
				}
			}

			results[count++] = part[index][currentIndex[index]];

			for(int i = 0; i < parts; i++){
				if(reachEnd.get(i)) continue;
				if(index == i) currentIndex[i]++;
				if(currentIndex[i] >= part[i].length) reachEnd.set(i);
			}
		}

		FlagsResult[] resultArray = Arrays.copyOf(results, count);

		return resultArray;
	}

	@Override
	public FlagsResult[] filter(long[] dummy, FlagsResult[] list){
		return list;
	}
}
